package com.maxlen.goods.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.maxlen.common.constant.ProductConstant;
import com.maxlen.common.utils.CopyUtils;
import com.maxlen.common.utils.PageUtils;
import com.maxlen.common.utils.Query;
import com.maxlen.goods.dao.CategoryDao;
import com.maxlen.goods.entity.CategoryEntity;
import com.maxlen.goods.entity.dto.CategoryByLimitTenDTO;
import com.maxlen.goods.entity.dto.SkuItemDTO;
import com.maxlen.goods.entity.vo.CategoryVO;
import com.maxlen.goods.entity.vo.CategorySortDto;
import com.maxlen.goods.entity.dto.CategoryDTO;
import com.maxlen.goods.service.CategoryBrandRelationService;
import com.maxlen.goods.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 分页查询所有菜单
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查询所有分类，遍历成树形
     * @return
     */
    @Override
    public List<CategoryDTO> listWithTree() {
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(CategoryEntity::getSort);
        List<CategoryEntity> list = baseMapper.selectList(wrapper);
        List<CategoryDTO> categoryVoList = CopyUtils.copyList(list, CategoryDTO.class);
        return getChildren(0L, categoryVoList);
    }

    /**
     * 根据分类 id 删除分类
     * @param asList
     */
    @Override
    public void removeCategoryByIds(List<Long> asList) {
        //TODO 1、检查当前删除的菜单，是否被别的地方引用
        baseMapper.deleteBatchIds(asList);
    }

    /**
     * 添加分类
     * @param categoryVO
     */
    @Override
    public void add(CategoryVO categoryVO) {
        CategoryEntity category = CopyUtils.copy(categoryVO, CategoryEntity.class);
        category.setShowStatus(1);
        baseMapper.insert(category);
    }

    /**
     * 修改分类
     * @param categoryVO
     */
    @Override
    @CacheEvict(value = "category", allEntries = true)
    @Transactional
    public void updateDetail(CategoryVO categoryVO) {
        CategoryEntity category = CopyUtils.copy(categoryVO, CategoryEntity.class);
        baseMapper.updateById(category);
        if(StringUtils.hasText(categoryVO.getName())){
            categoryBrandRelationService.updateCategory(categoryVO.getCatId(), categoryVO.getName());
        }
    }

    /**
     * 批量修改
     * @param categorySortDto
     */
    @Override
    public void updateMultipartById(List<CategorySortDto> categorySortDto) {
        List<CategoryEntity> categoryEntityList = CopyUtils.copyList(categorySortDto, CategoryEntity.class);
        this.updateBatchById(categoryEntityList);
    }

    /**
     * 获取某个分类 id 的所有父类 id
     * @param catelogId
     * @return
     */
    @Override
    public Long[] findPathById(Long catelogId) {
        List<Long> ids = new ArrayList<>();
        CategoryEntity categoryEntity = baseMapper.selectById(catelogId);
        Long parentCid = categoryEntity.getParentCid();
        if(!parentCid.equals(0L)){
            CategoryEntity category = baseMapper.selectById(parentCid);
            ids.add(category.getParentCid());
        }
        ids.add(parentCid);
        ids.add(catelogId);
        return ids.toArray(new Long[ids.size()]);
    }

    /**
     * 查询前十个一级分类
     * @return
     */
    @Override
    @Cacheable(value = "category", key = "#root.method.name", sync = true)
    public List<CategoryEntity> getLevelOneCategoryList() {
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(CategoryEntity::getSort);
        wrapper.eq(CategoryEntity::getCatLevel, ProductConstant.CategoryLevel.LEVEL_ONE.getCode());
        wrapper.eq(CategoryEntity::getShowStatus, ProductConstant.ShowStatus.SHOW_OK.getCode());
        wrapper.last("limit " + ProductConstant.CategoryLevel.LEVEL_LBT_COUNT.getCode());
        wrapper.select(CategoryEntity::getCatId, CategoryEntity::getName);
        return baseMapper.selectList(wrapper);
    }

    /**
     * 查询前十个所有分类，遍历成树形
     * @return
     */
    @Override
    //TODO @Cacheable存取树形对象出现问题
//    @Cacheable(value = "category", key = "#root.method.name", sync = true)
    public List<CategoryByLimitTenDTO> getCategoryByTreeWithTen() {

        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(CategoryEntity::getSort);
        wrapper.eq(CategoryEntity::getShowStatus, ProductConstant.ShowStatus.SHOW_OK.getCode());
        List<CategoryEntity> list = baseMapper.selectList(wrapper);
        List<CategoryByLimitTenDTO> categoryVoList = CopyUtils.copyList(list, CategoryByLimitTenDTO.class);

        List<CategoryByLimitTenDTO> dtoList = getCategoryByLimitTenChildren(0L, categoryVoList);

        List<CategoryByLimitTenDTO> tenDTOList = new ArrayList<>();
        if(dtoList.size() > ProductConstant.CategoryLevel.LEVEL_LBT_COUNT.getCode()){
            tenDTOList = dtoList.subList(0, ProductConstant.CategoryLevel.LEVEL_LBT_COUNT.getCode());
        }
        stringRedisTemplate.opsForValue().set("category:categoryByTreeWithTenJSON", JSON.toJSONString(tenDTOList), 360000, TimeUnit.MILLISECONDS);
        String category = stringRedisTemplate.opsForValue().get("category:categoryByTreeWithTenJSON");
        if(StringUtils.hasText(category)){
            System.out.println((List<CategoryByLimitTenDTO>) JSON.parse(category));
        }
        return tenDTOList;

    }

    @Override
    public List<SkuItemDTO.CategoryDetailDTO> getCategoryByIdWithAll(Long catalogId) {

        List<SkuItemDTO.CategoryDetailDTO> category = new ArrayList<>();

        CategoryEntity category1 = baseMapper.selectById(catalogId);
        SkuItemDTO.CategoryDetailDTO categoryDetailDTO1 = new SkuItemDTO.CategoryDetailDTO();
        categoryDetailDTO1.setName(category1.getName());
        categoryDetailDTO1.setCatId(category1.getCatId());
        category.add(categoryDetailDTO1);

        if(!category1.getParentCid().equals(0L)){

            CategoryEntity category2 = baseMapper.selectById(category1.getParentCid());
            SkuItemDTO.CategoryDetailDTO categoryDetailDTO2 = new SkuItemDTO.CategoryDetailDTO();
            categoryDetailDTO2.setName(category2.getName());
            category.add(categoryDetailDTO2);

            if(!category2.getParentCid().equals(0L)){

                CategoryEntity category3 = baseMapper.selectById(category2.getParentCid());
                SkuItemDTO.CategoryDetailDTO categoryDetailDTO3 = new SkuItemDTO.CategoryDetailDTO();
                categoryDetailDTO3.setName(category3.getName());
                category.add(categoryDetailDTO3);

            }
        }

        return category;

    }

    /**
     * 本地锁与分布式锁的实现
     */
    // 返回前十个的所有分类信息
//    public List<CategoryByLimitTenDTO> getCategory(){
//        String category = stringRedisTemplate.opsForValue().get("categoryByTreeWithTenJSON");
//        if(StringUtils.hasText(category)){
//            return (List<CategoryByLimitTenDTO>) JSON.parse(category);
//        }
//        System.out.println("查询数据库。。。。。。");
//        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
//        wrapper.orderByAsc(CategoryEntity::getSort);
//        wrapper.eq(CategoryEntity::getShowStatus, ProductConstant.ShowStatus.SHOW_OK.getCode());
//        List<CategoryEntity> list = baseMapper.selectList(wrapper);
//        List<CategoryByLimitTenDTO> categoryVoList = CopyUtils.copyList(list, CategoryByLimitTenDTO.class);
//
//        List<CategoryByLimitTenDTO> dtoList = getCategoryByLimitTenChildren(0L, categoryVoList);
//
//        List<CategoryByLimitTenDTO> tenDTOList = new ArrayList<>();
//        if(dtoList.size() > ProductConstant.CategoryLevel.LEVEL_LBT_COUNT.getCode()){
//            tenDTOList = dtoList.subList(0, ProductConstant.CategoryLevel.LEVEL_LBT_COUNT.getCode());
//        }
//
//        stringRedisTemplate.opsForValue().set("categoryByTreeWithTenJSON", JSON.toJSONString(tenDTOList));
//        return tenDTOList;
//    }

    // 本地锁
//    public List<CategoryByLimitTenDTO> getCategoryByLocalLock(){
//        synchronized (this){
//            return getCategory();
//        }
//    }

    // Redis分布式锁
//    public List<CategoryByLimitTenDTO> getCategoryByRedisLock(){
//        // （1）为了防止在执行获得锁成功的方法内出现异常或者其他原因导致没有删除锁，导致其他线程死锁问题，要给 lock 加个过期时间
//        // （2）假设 A 线程在 getCategory() 业务时间过长，直到锁过期时间到了，这时候 B 线程拿到锁，但是这个时候 A 线程业务执行完后，执行了删除锁
//        //      ，导致删除了 B 获取得到的锁
//        // （2.1）生成 uuid 为 lock 的值，当要删除锁的时候判断，拿到锁的 value 是否相同，相同就删除
//        String uuid = UUID.randomUUID().toString();
//        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
//        if(lock){
//            System.out.println("获取分布式锁成功...");
//            List<CategoryByLimitTenDTO> category = new ArrayList<>();
//
//            try{
//                category = getCategory();
//            }finally {
//
//                // 不符合原子操作，如果拿取锁，Redis 在传输给服务端的时候，刚好过期了，其他线程又来创建锁，那么虽然拿到的值是自己的，但是还是会删除
//                // 掉其他线程的锁
//                /*String lockValue = stringRedisTemplate.opsForValue().get("lock");
//                if (uuid.equals(lockValue)){
//                    stringRedisTemplate.delete("lock");
//                }*/
//
//                // 原子删除锁
//                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
//                stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
//            }
//
//            return category;
//        }else{
//            System.out.println("获取分布式锁失败...");
//            // 自旋
//            try{
//                Thread.sleep(200);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            return getCategoryByRedisLock();
//        }
//    }

    // 遍历成树形
    private List<CategoryDTO> getChildren(Long id, List<CategoryDTO> categoryVoList){
        List<CategoryDTO> categoryVos = new ArrayList<>();
        for (CategoryDTO categoryVo : categoryVoList) {
            if(id.equals(categoryVo.getParentCid())){
                categoryVo.setChildren(getChildren(categoryVo.getCatId(), categoryVoList));
                categoryVos.add(categoryVo);
            }
        }
        return categoryVos;
    }

    // 遍历成树形，返回前十个分类
    private List<CategoryByLimitTenDTO> getCategoryByLimitTenChildren(Long id, List<CategoryByLimitTenDTO> categoryVoList){
        List<CategoryByLimitTenDTO> categoryVos = new ArrayList<>();
        for (CategoryByLimitTenDTO categoryVo : categoryVoList) {
            if(id.equals(categoryVo.getParentCid())){
                categoryVo.setChildren(getCategoryByLimitTenChildren(categoryVo.getCatId(), categoryVoList));
                categoryVos.add(categoryVo);
            }
        }
        return categoryVos;
    }

}
